#include "ModelLoader.h"

#include <fstream>
#include <sstream>
#include <vector>

#include <ezlogger_headers.hpp>

namespace Crows3D
{
   ModelLoader::ModelLoader(void)
   {
   }

   ModelLoader::~ModelLoader(void)
   {
   }

   bool ModelLoader::LoadObjModel(Device *dev, char* filename, Model* model)
   {
      //EZLOGGERSTREAM << "Loading Model from file: " << filename << std::endl;
      std::wifstream file;
      std::string line = "";

      std::vector<VertexPositionTextureNormal>  verts;
      std::vector<D3DXVECTOR4>                  positions;
      std::vector<D3DXVECTOR2>                  uvCoords;
      std::vector<D3DXVECTOR3>                  normals;
      std::vector<unsigned long>                indices;

      Mesh*                         mesh = NULL;
      Texture*                      texture = NULL;
      EffectPositionTextureNormal*  effect = NULL;


      unsigned long curIndex = 0;

      int subset = 0;
      

      file.open(filename, std::ifstream::in);
      if (!file.is_open())
      {
         //EZLOGGERSTREAM << "ERROR: couldn't open model file: " << filename << std::endl;
         return false;
      }

      // TESTING
      #if 0
      {
         // Initialize our mesh
         mesh = new Mesh();

         // Load the new texture
         //std::string strTexture(";
         //linestream >> strTexture;
         //strTexture = ".\\Resources\\Textures\\" + strTexture + ".bmp";

         //std::wstring wstrTexture;
         //wstrTexture.assign(strTexture.begin(), strTexture.end());

         texture = new Texture();
         texture->Initialize(dev, L".\\Resources\\Textures\\spstob_1.bmp");


         // Create a default effect for now
         effect = new EffectPositionTextureNormal();
         effect->LoadDefaults(dev);
      }
      #endif

      while (!file.eof())//getline(file, line))
      {
         std::wstring type;
         float x, y, z;
         float u, v;
         float nX, nY, nZ;

         //std::istringstream linestream(line);

         // read the data from this line
         file >> type;

         if (type.compare(L"v") == 0) // position
         {
            file >> x >> y >> z;
            
            D3DXVECTOR4 vertPos;
            vertPos.x = x;
            vertPos.y = y;
            vertPos.z = z;
            vertPos.w = 0.0f;

            positions.push_back(vertPos);
         }     
         else if (type.compare(L"vt") == 0) // uv coordinate
         {
            file >> u >> v;
            
            D3DXVECTOR2 vertUV;
            vertUV.x = u;
            vertUV.y = 1.0f - v;

            uvCoords.push_back(vertUV);
         }
         else if (type.compare(L"vn") == 0) // uv coordinate
         {
            file >> nX >> nY >> nZ;
            
            D3DXVECTOR3 vertNorm;
            vertNorm.x = nX;
            vertNorm.y = nY;
            vertNorm.z = nZ;

            normals.push_back(vertNorm);
         }
         else if (type.compare(L"f") == 0)
         {
            bool noNormal = false;
            unsigned long vertsToNormalize[3];

            // Get each vertex
            for (int i = 0; i < 3; ++i)
            {
               // Read the vertex
               std::wstring sVert;
               //std::stringstream vertStream(sVert);

               //std::wstring vert;
               wchar_t  buffer[100];
               wchar_t* value;
               unsigned long iPos, iUV, iNorm; 

               // Read the indices that define each vertex
               file >> buffer;

               VertexPositionTextureNormal newVert;


               // split out each specific index and put the correct data in our list
               value = wcstok(buffer, L"/");
               iPos = _wtoi(value);
               newVert.position = positions[iPos - 1];

               // UV
               value = wcstok(NULL, L"/");
               if (!value) // No texture component
               {
                  newVert.texture = D3DXVECTOR2(0,0);
               }
               else
               {
                  iUV = _wtoi(value);
                  newVert.texture = uvCoords[iUV - 1];//D3DXVECTOR2(0,0);
               }

               // Normal
               value = wcstok(NULL, L"/");
               if (!value) // No normal component
               {
                  noNormal = true;
                  newVert.normal = D3DXVECTOR3(0,0,-1);
                  vertsToNormalize[i] = iPos-1;
               }
               else
               {
                  iNorm = _wtoi(value);
                  newVert.normal = normals[iNorm - 1];
               }
               
               
               
               
               

               verts.push_back(newVert);
               indices.push_back(curIndex);
               curIndex++;
            }

            if (noNormal)
            {
               D3DXVECTOR3 v1, v2, v3;
               D3DXVECTOR3 vec1, vec2;
               D3DXVECTOR3 normal;

               v1 = (D3DXVECTOR3)positions[vertsToNormalize[0]];
               v2 = (D3DXVECTOR3)positions[vertsToNormalize[1]];
               v3 = (D3DXVECTOR3)positions[vertsToNormalize[2]];

               vec1 = v2 - v1;
               vec2 = v3 - v1;

               D3DXVec3Cross(&normal, &vec1, &vec2);
               D3DXVec3Normalize(&normal, &normal);

               verts[curIndex - 3].normal = verts[curIndex - 2].normal = verts[curIndex - 1].normal = normal;
            }
         }
         else if (type.compare(L"usemtl") == 0)
         {
            // If we already have some data, initialize the mesh and add it to the model
            if (mesh != NULL)
            {
               // Create the mesh!
               mesh->Initialize(dev, verts.size(), &verts[0], indices.size(), &indices[0]);

               model->AddSubset(dev, mesh, effect, texture);

               mesh = NULL;
               texture = NULL;
               effect = NULL;

               verts.clear();
               indices.clear();
               curIndex = 0;
            }

            // Initialize our mesh
            mesh = new Mesh();



            // Load the new texture
            std::wstring strTexture;
            file >> strTexture;
            strTexture = L".\\Resources\\Textures\\" + strTexture;

            //std::wstring wstrTexture(strTexture.length(), L' ');
            //wstrTexture.assign(strTexture.begin(), strTexture.end());
            //std::copy(strTexture.begin(), strTexture.end(), wstrTexture.begin());

            texture = new Texture();
            if (!texture->Initialize(dev, strTexture))
            {
               EZLOGGERSTREAM << "ERROR: couldn't create texture from file:" /*<< strTexture*/ << std::endl;
               return false;
            }


            // Create a default effect for now
            effect = new EffectPositionTextureNormal();
            if (!effect->LoadDefaults(dev))
            {
               EZLOGGERSTREAM << "ERROR: couldn't create default effect" << std::endl;
               return false;
            }
         }
         else
         {
            // do nothing
         }

      }


      file.close();

      // If we already have some data, initialize the mesh and add it to the model
      if (mesh != NULL)
      {
         // Create the mesh!
         mesh->Initialize(dev, verts.size(), &verts[0], indices.size(), &indices[0]);

         model->AddSubset(dev, mesh, effect, texture);

         mesh = NULL;
         texture = NULL;
         effect = NULL;
      }


      return true;
   }
}